package ferp.center.server.servlet.game;

import com.google.gson2.JsonSyntaxException;
import ferp.center.network.request.RequestGameReportSend;
import ferp.center.server.Action;
import ferp.center.server.entity.Device;
import ferp.center.server.entity.Report;
import ferp.core.card.Card;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.game.Trick;
import ferp.core.player.Hand;
import ferp.core.player.Profile;

import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;

/**
 * User: igorgok
 * Date: 10/26/12 7:47 PM
 */
public class GameReportSend extends HttpServlet
{
  @Override
  protected void doPost(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    Action.execute(request, response, RequestGameReportSend.class, new Action.Write<RequestGameReportSend, Boolean>()
    {
      @SuppressWarnings("unchecked")
      public Boolean run(EntityManager em, RequestGameReportSend input) throws IOException
      {
        // automatic reports have email set to "n/a"
        boolean custom = !input.email.equals("n/a");
        Report r = createAndStoreReport(em, input, custom);

        // try to email only custom reports
        if (custom)
          try
          {
            Device.Location location = new Device.Location(request);
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);
            Message msg = new MimeMessage(session);

            msg.setFrom(new InternetAddress("playonsmart@gmail.com", "FERP Center"));
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress("playonsmart@gmail.com", "Dev. Team"));
            msg.setSubject("Faulty game report [" + input.centerId + ']');

            StringBuilder sb = new StringBuilder(2048);

            sb.append("From: ").append(input.name).append(" (").append(input.email).append(")\n")
              .append("Report ID: ").append(r.getKey().getId()).append('\n')
              .append("Location: ").append(location.country).append('/').append(location.region).append('/').append(location.city).append('\n')
              .append("App version: ").append(input.version).append('\n')
              .append("Description: ").append(input.description).append('\n')
              .append("Exception: ").append(input.exception).append('\n');

            // settings information
            if (input.settings != null && input.settings.length() > 0)
              dumpSettings(sb, input);

            // add game information
            // old version
            if (input.game != null && input.game.length() > 0)
              dumpGameDetails(sb, "old format", Action.gson().fromJson(input.game, Game.class), false);
            // new version
            if (input.games != null && input.games.length() > 0)
            {
              Profile.Games games = Action.gson().fromJson(input.games, Profile.Games.class);

              if (games.actual != null)
                dumpGameDetails(sb, "actual", games.actual, false);

              if (games.replay != null)
                dumpGameDetails(sb, "replay", games.replay, false);

              //if (games.initial != null)
              //  dumpGameDetails(sb, "initial", games.initial, true);
            }

            msg.setText(sb.toString());

            Transport.send(msg);
          }
          catch (Exception e)
          {
            Action.logger().severe(e.getMessage());

            r.setSent(false);
            em.persist(r);
            em.flush();
          }

        return r.getSent();
      }
    });
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  {
  }

  private Report createAndStoreReport(EntityManager em, RequestGameReportSend input, boolean custom)
  {
    Report r = new Report();

    r.setCreated(new Date());
    r.setAppVersion(input.version);
    r.setName(input.name);
    r.setEmail(input.email);
    r.setDescription(input.description);
    r.setSent(custom);

    if (input.game != null)
    {
      Profile.Games games = new Profile.Games();

      games.actual = Action.gson().fromJson(input.game, Game.class);

      r.setSnapshot(Action.gson().toJson(games));
    }

    if (input.games != null)
      r.setSnapshot(input.games);

    r.setSettings(input.settings);
    r.setStatistics(input.statistics);
    r.setException(input.exception);
    r.setService(Action.gson().toJson(input.service));
    //r.setHand(Action.gson().toJson(new Hand(input.cvs, input.candidates)));

    em.persist(r);
    em.flush();

    return r;
  }

  private static void dumpSettings(StringBuilder sb, RequestGameReportSend input)
  {
    try
    {
      Settings s = Action.gson().fromJson(input.settings, Settings.class);

      if (s != null)
      {
        sb.append("\n--- Settings: ---\n")
            .append("Opponent levels: ").append(s.levels[0]).append('&').append(s.levels[1]).append('\n')
            .append("Game type: ").append(s.gameFlavor).append('\n')
            .append("Pass: ").append(s.passingType).append('/').append(s.passingProgression).append('/').append(s.passingTrickCost).append('/').append(s.passingExit).append('\n')
            .append("Whist: ").append(s.whistType);

        if (s.whistOn6Spades == Settings.WhistOn6Spades.MANDATORY)
          sb.append("&6♠");

        sb.append('\n').append("Move by: ").append(s.cardMovingMode).append('\n');
      }
    }
    catch (JsonSyntaxException e)
    {
      sb.append("\n--- Error loading settings ---\n");
    }
  }

  private static void dumpGameDetails(StringBuilder sb, String what, Game g, boolean initial)
  {
    sb.append("\n--- Game information: ")
      .append(what)
      .append(" ---\n")
      .append("Scenario: ").append(g.scenario).append('\n')
      .append("Mode: ").append(g.mode).append('\n')
      .append("State: ").append(g.state).append('\n')
      .append("Sets: ").append(g.sets).append('\n')
      .append("Consequent passing sets: ").append(g.consequentPassingSets).append('\n')
      .append("Dealer: ").append(g.player.dealer).append('\n')
      .append("First hand: ").append(Game.next(g.player.dealer)).append('\n')
      .append("Current hand: ").append(g.hand).append('\n');

    sb.append("Deal:\n");
    for (int p = 0; p < Game.PLAYERS; ++p)
      sb.append("  #").append(p).append("> ").append(Hand.dump(g.player.hands[p].initial)).append('\n');

    sb.append("Talon: ").append(Card.card(g.talon()[0])).append(' ').append(Card.card(g.talon()[1])).append('\n');

    sb.append("Bids:\n");
    for (int p = 0; p < Game.PLAYERS; ++p)
      sb.append("  #").append(p).append("> ")
        .append(g.player.bids[p].current == null ? "none" : g.player.bids[p].current)
        .append('/')
        .append(g.player.bids[p].max == null ? "none" : g.player.bids[p].max)
        .append('\n');

    if (g.bid != null && g.player.declarer != Game.NO_ID)
      sb.append("Winning bid: ").append(g.bid).append(" by p").append(g.player.declarer).append('\n');

    boolean drop = g.drop()[0] != 0 && g.drop()[1] != 0;

    if (drop)
      sb.append("Drop: ").append(Card.card(g.drop()[0])).append(' ').append(Card.card(g.drop()[1])).append('\n');

    if (g.player.declarer != Game.NO_ID)
      sb.append("Contract: ").append(g.getBid(g.player.declarer).current).append(" by p").append(g.player.declarer).append('\n');
    else
      sb.append("Passing game\n");

    if (drop)
    {
      sb.append("Initial position:\n");

      for (int p = 0; p < Game.PLAYERS; ++p)
        sb.append("  #").append(p).append("> ")
            .append(Hand.dump(p == g.player.declarer ? Hand.adjust(g.player.hands[p].initial, g.talon(), g.drop()) : g.player.hands[p].initial))
            .append('\n');
    }

    if (!initial && (g.rounds > 0 || g.trick.current != 0))
    {
      ArrayList<Integer> tricks = g.trick.list();

      //sb.append("Round: ").append(g.rounds).append('\n');

      sb.append("Tricks:\n");

      for (int i = 0; i < tricks.size(); ++i)
        sb.append("  #").append(i).append("> ").append(Trick.dump(tricks.get(i), g.trump())).append('\n');

      // current trick
      if (g.rounds < 10)
        sb.append("  #").append(g.rounds).append("> ").append(Trick.dump(g.trick.current, g.trump())).append('\n');

      if (g.rounds < 9)
      {
        sb.append("Current position:\n");

        for (int p = 0; p < Game.PLAYERS; ++p)
          sb.append("  #").append(p).append("> ").append(Hand.dump(g.player.hands[p].current)).append('\n');
      }
      else
        sb.append("---\n\n");
    }

    // log
    if (g.log != null && g.log.size() > 0)
    {
      sb.append("Log:\n");

      for (String e : g.log)
        sb.append("  ").append(e).append('\n');
    }
  }

/*
  private static final class Hand
  {
    List<RequestGameReportSend.CardViewData> cvs;
    String candidates;

    Hand(List<RequestGameReportSend.CardViewData> cvs, String candidates)
    {
      this.cvs = cvs;
      this.candidates = candidates;
    }
  }
*/
}
